package gopacket

import (
	"bufio"
	"bytes"
	"context"
	"crypto/tls"
	"fmt"
	"github.com/google/gopacket"
	"github.com/google/gopacket/layers"
	"github.com/google/gopacket/pcap"
	"net/http"
	"strings"
	"time"
)

// ListDevs 查看所有网络接口
func ListDevs() []pcap.Interface {
	// 得到所有的(网络)设备
	devices, err := pcap.FindAllDevs()
	if err != nil {
		fmt.Println(err)
		return nil
	}
	return devices
}

func Dump() {
	var (
		device      string = "\\Device\\NPF_{C2BCFD65-FE5E-4870-B60B-EB99F308BF2B}"
		snapshotLen int32  = 1 << 18 // 1Mb
		promiscuous bool   = true
		err         error
		timeout     time.Duration = 30 * time.Second
		handle      *pcap.Handle
	)
	handle, err = pcap.OpenLive(device, snapshotLen, promiscuous, timeout)
	if err != nil {
		fmt.Println(err)
	}
	defer handle.Close()
	// Use the handle as a packet source to process all packets
	packetSource := gopacket.NewPacketSource(handle, handle.LinkType())

	var (
		payload    = []byte{}
		contentLen int64
		resp       *http.Response
		req        *http.Request

		printBody = false
		keyFile   = ""
		certFile  = ""
	)
	for packet := range packetSource.Packets() {
		if tlsLayer := packet.Layer(layers.LayerTypeTLS); tlsLayer != nil {
			config := &tls.Config{}
			if !strSliceContains(config.NextProtos, "http/1.1") {
				config.NextProtos = append(config.NextProtos, "http/1.1")
			}

			configHasCert := len(config.Certificates) > 0 || config.GetCertificate != nil
			if !configHasCert || certFile != "" || keyFile != "" {
				config.Certificates = make([]tls.Certificate, 1)
				config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile)
				if err != nil {
					fmt.Println(err.Error())
				}
			}

			conn := &tls.Conn{}
			ctx := context.Background()
			err = conn.HandshakeContext(ctx)

		}

		if tcpLayer := packet.Layer(layers.LayerTypeTCP); tcpLayer != nil {
			if packet.ApplicationLayer() != nil {
				tcp := tcpLayer.(*layers.TCP)
				if tcp != nil && tcp.Payload != nil {
					segPayload := tcp.Payload

					if bytes.HasPrefix(segPayload, []byte("HTTP/")) {
						resp, err = readHttpResp(segPayload)
						if err != nil {
							continue
						}
						contentLen = resp.ContentLength
						if contentLen > int64(snapshotLen) {
							contentLen = int64(snapshotLen)
						}
						payload = make([]byte, len(segPayload))
						copy(payload, segPayload)
						printBody = len(resp.Header["Content-Type"]) > 0 &&
							strings.Contains(resp.Header["Content-Type"][0], "application/json")
					} else if strings.HasPrefix(string(segPayload), http.MethodGet+" ") ||
						strings.HasPrefix(string(segPayload), http.MethodHead+" ") ||
						strings.HasPrefix(string(segPayload), http.MethodPost+" ") ||
						strings.HasPrefix(string(segPayload), http.MethodPut+" ") ||
						strings.HasPrefix(string(segPayload), http.MethodPatch+" ") ||
						strings.HasPrefix(string(segPayload), http.MethodDelete+" ") ||
						strings.HasPrefix(string(segPayload), http.MethodConnect+" ") ||
						strings.HasPrefix(string(segPayload), http.MethodOptions+" ") ||
						strings.HasPrefix(string(segPayload), http.MethodTrace+" ") {
						req, err = readHttpReq(segPayload)
						if err != nil {
							continue
						}
						contentLen = req.ContentLength
						if contentLen > int64(snapshotLen) {
							contentLen = int64(snapshotLen)
						}
						payload = make([]byte, len(segPayload))
						copy(payload, segPayload)
						printBody = true
					} else if len(segPayload) > 0 {
						payload = append(payload, segPayload...)
					}

					if err == nil && contentLen > 0 {
						split := strings.Split(string(segPayload), "\r\n\r\n")
						segLen := len(split[0])
						if len(split) > 1 {
							segLen = len(split[1])
						}
						if contentLen -= int64(segLen); contentLen <= 0 {
							split = strings.Split(string(payload), "\r\n\r\n")
							fmt.Println("---------------------------")
							fmt.Println(split[0])
							if len(split) > 1 && printBody {
								//by := split[1]
								// 打印响应体
								//fmt.Println(len(by), by)
							}
							contentLen = 0
							payload = payload[:0]
						}
					}
				}
			}
		}
	}
}

func strSliceContains(ss []string, s string) bool {
	for _, v := range ss {
		if v == s {
			return true
		}
	}
	return false
}

// readHttpResp 读取 http 响应
func readHttpResp(payload []byte) (*http.Response, error) {
	br := bufio.NewReader(bytes.NewReader(payload))
	return http.ReadResponse(br, nil)
}

// readHttpReq 读取 http 响应
func readHttpReq(payload []byte) (*http.Request, error) {
	br := bufio.NewReader(bytes.NewReader(payload))
	return http.ReadRequest(br)
}
